/*
 * Copyright (c) 2004-2006, Volatile-Engine All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the Volatile-Engine nor the
 * names of its contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
/**
 * 
 */
package com.volatileengine.eventqueue;

import java.util.LinkedList;

/**
 * 
 * EventQueue for queuing &lt;T&gt; types to be processed. Implemeting classes
 * should be careful in not calling processAll() or processLast() from the
 * concrete processCurrentEvent(T) method as this will result in infinite
 * recursive behaviour.
 * 
 * @author Administrator
 * 
 */
public abstract class EventQueue<T> {

	// read write queues
	private LinkedList<T>[] eventQueues;
	// index of write queue
	private int writeQueue;

	// the read responsible for processing this EventQueue
	private Thread eventHandlingThread;

	private boolean enabled;

	@SuppressWarnings("unchecked")
	/*
	 * * Creates the EventQueue as well as the LinkedList<T> queues
	 */
	public EventQueue() {
		eventQueues = new LinkedList[2];
		for (int i = 0; i < 2; i++) {
			eventQueues[i] = new LinkedList<T>();
		}
	}

	/**
	 * Set the thread that will handle the addition and running of this
	 * EventQueue
	 * 
	 * @param eventHandlingThread
	 */
	public void setEventHandlingThread(Thread eventHandlingThread) {
		this.eventHandlingThread = eventHandlingThread;
	}

	public void processAll() {
		if (enabled) {
			if (Thread.currentThread() != eventHandlingThread) {
				throw new IllegalStateException("Thread: " + Thread.currentThread().getName()
						+ " is not the owner of this EventQueue");
			}
			LinkedList<T> queue = switchQueue();
			synchronized (queue) {
				while (!queue.isEmpty()) {
					T event = queue.removeFirst();
					processCurrentEvent(event);
				}
			}
		}
	}

	/**
	 * Run &lt;T&gt;
	 * 
	 * @param event
	 */
	protected abstract void processCurrentEvent(T event);

	public boolean isEmpty() {
		LinkedList<T> queue = switchQueue();
		return queue.isEmpty();
	}

	public void addEvent(T runnable) {
		if (Thread.currentThread() == eventHandlingThread) {
			processCurrentEvent(runnable);
		} else {
			synchronized (eventQueues) {
				eventQueues[writeQueue].add(runnable);
			}
		}
	}

	private LinkedList<T> switchQueue() {
		synchronized (eventQueues) {
			writeQueue = 1 - writeQueue;
			return eventQueues[1 - writeQueue];
		}
	}

	public boolean isEnabled() {
		return enabled;
	}

	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}

}
